#ifndef AST_HPP_INCLUDED
#define AST_HPP_INCLUDED

class Visitor;
class IDSymbol;

class node_Exp{
public:
    virtual int acceptVisitor(Visitor *v) = 0;
};

class node_ExpList{
public:
    node_Exp *exp;
    node_ExpList *nextExp;
public:
    node_ExpList(node_Exp *Exp, node_ExpList *NextExp);
    ~node_ExpList();
    int acceptVisitor(Visitor *v);
};

class node_Type{
public:
    char *className; //Caso o tipo da variavel seja uma classe, mas nao pode ser arranjo
    int primitiveType; //Caso o tipo da variavel seja um tipo primitivo da linguagem
    bool isArray; //Caso o tipo primitivo seja um arranjo

public:
    node_Type(char *ClassName, int PrimitiveType, bool isArray);
    ~node_Type();
    int acceptVisitor(Visitor *v);
};

class node_NameDecl{
public:
    node_Type *varType;
    char* varName;
    int line;
public:
    node_NameDecl(node_Type *varType, char* varName, int line);
    ~node_NameDecl();
    int acceptVisitor(Visitor *v);
};

class node_Name{
public:
    //NextName representa proximo metodo/atributo.
    int line;
    char* name;
    bool length;
    bool isThis;
    node_ExpList* sentParamsExpList;
    node_Name* nextName;
    bool isArray; //Caso seja um arranjo
    bool isMethod;
    node_Exp* index; //se for um arranjo, qual foi o índice acessado
    IDSymbol *tabLink;
    int tabLinkType;
public:
    node_Name(char* VarName, bool length, bool isThis, node_ExpList* SentParamsExpList, node_Name* NextName, bool isArray, node_Exp* index, int line);
    ~node_Name();
    int acceptVisitor(Visitor *v);
};

class node_NameExp : public node_Exp{
public:
    node_Name *Name;
public:
    node_NameExp(node_Name *Name);
    ~node_NameExp();
    int acceptVisitor(Visitor *v);
};

class node_Literal : public node_Exp{
public:
    char *literal;
    int line;
public:
    node_Literal(char *Literal, int line);
    ~node_Literal();
    int acceptVisitor(Visitor *v);
};

class node_Logic : public node_Exp{ // FALSE ou TRUE
public:
    int logic;
    int line;
public:
    node_Logic(int Logic, int line);
    ~node_Logic();
    int acceptVisitor(Visitor *v);
};

class node_Number : public node_Exp{ // Número Real(float) ou inteiro(int)
public:
    int type;
    char *number;
    int line;

public:
    node_Number(int Type, char *Number, int line);
    ~node_Number();
    int acceptVisitor(Visitor *v);
};

class node_RelationalOP : public node_Exp{ // Operadores relacionais "==", "!=", "&&" e "||"
public:
    int relOp;
    node_Exp *leftExp;
    node_Exp *rightExp;
    int line;
public:
    node_RelationalOP(int RelOp, node_Exp *LeftExp, node_Exp *RightExp, int line);
    ~node_RelationalOP();
    int acceptVisitor(Visitor *v);
};

class node_BinOp : public node_Exp{ // Adição e subtração
public:
    int binOp;
    node_Exp *leftExp;
    node_Exp *rightExp;
    int line;
public:
    node_BinOp(int BinOp, node_Exp *LeftExp, node_Exp *RightExp, int line);
    ~node_BinOp();
    int acceptVisitor(Visitor *v);
};

class node_Bin2Op : public node_Exp{ // Multiplição, resto e divisão
public:
    int bin2Op;
    node_Exp *leftExp;
    node_Exp *rightExp;
    int line;
public:
    node_Bin2Op(int Bin2Op, node_Exp *LeftExp, node_Exp *RightExp, int line);
    ~node_Bin2Op();
    int acceptVisitor(Visitor *v);
};

class node_BooleanOP : public node_Exp{ // Operadores booleanos ">", ">=", "<", "<="
public:
    int boolOp;
    node_Exp *leftExp;
    node_Exp *rightExp;
    int line;
public:
    node_BooleanOP(int BoolOp, node_Exp *LeftExp, node_Exp *RightExp, int line);
    ~node_BooleanOP();
    int acceptVisitor(Visitor *v);
};

class node_BitwiseOP : public node_Exp{ // OR e AND bit a bit
public:
    int bitOp;
    node_Exp *leftExp;
    node_Exp *rightExp;
    int line;
public:
    node_BitwiseOP(int BitOp, node_Exp *LeftExp, node_Exp *RightExp, int line);
    ~node_BitwiseOP();
    int acceptVisitor(Visitor *v);
};

class node_Not : public node_Exp{ // Sinal un·rio "!"
public:
    node_Exp *exp;
    int line;
public:
    node_Not(node_Exp *Exp, int line);
    ~node_Not();
    int acceptVisitor(Visitor *v);
};

class node_Sign : public node_Exp{ // Sinais unários "+" e "-"
public:
    int sign;
    node_Exp *exp;
    int line;
public:
    node_Sign(int Sign, node_Exp *Exp, int line);
    ~node_Sign();
    int acceptVisitor(Visitor *v);
};

class node_New : public node_Exp{
public:
    node_Type *type;
    node_Exp *arrayLengthExp;
    int line;
public:
    node_New(node_Type *Type, node_Exp *ArrayLength, int line);
    ~node_New();
    int acceptVisitor(Visitor *v);
};

class node_Stmt{
public:
    virtual int acceptVisitor(Visitor *v) = 0;
};

class node_StmtList{
public:
    node_Stmt *stmt;
    node_StmtList *nextStmt;
public:
    node_StmtList(node_Stmt *Stmt, node_StmtList *NextStmt);
    ~node_StmtList();
    int acceptVisitor(Visitor *v);
};

class node_While: public node_Stmt{
public:
    node_Exp *condition;
    node_StmtList *stmtList;
    int line;
public:
    node_While(node_Exp *Condition, node_StmtList *StmtList, int line);
    ~node_While();
    int acceptVisitor(Visitor *v);
};

class node_If: public node_Stmt{
public:
    node_Exp *condition;
    node_StmtList *stmtList;
    node_StmtList *elseStmtList;
    int line;
public:
    node_If(node_Exp *Condition, node_StmtList *StmtList, node_StmtList *ElseStmtList, int line);
    ~node_If();
    int acceptVisitor(Visitor *v);
};

class node_Println: public node_Stmt{
public:
    node_ExpList *expList;
    int line;
public:
    node_Println(node_ExpList *ExpList, int line);
    ~node_Println();
    int acceptVisitor(Visitor *v);
};

class node_Read: public node_Stmt{
public:
    node_Exp *exp;
    int line;
public:
    node_Read(node_Exp *Exp, int line);
    ~node_Read();
    int acceptVisitor(Visitor *v);
};

class node_Return: public node_Stmt{
public:
    node_Exp *exp;
    int line;
public:
    node_Return(node_Exp *Exp, int line);
    ~node_Return();
    int acceptVisitor(Visitor *v);
};

class node_Assign: public node_Stmt{
public:
    node_Name *Name;
    node_Exp *assignmentExp;
    int line;
public:
    node_Assign(node_Name *Name, node_Exp *AssignmentExp, int line);
    ~node_Assign();
    int acceptVisitor(Visitor *v);
};

class node_Call: public node_Stmt{
public:
    node_Name *callMethod;
    int line;
public:
    node_Call(node_Name *callMethod, int line);
    ~node_Call();
    int acceptVisitor(Visitor *v);
};

class node_LocalDecl{
public:
    node_LocalDecl *nextDecl;
public:
    node_LocalDecl(node_LocalDecl *nextDecl);
    virtual int acceptVisitor(Visitor *v) = 0;
    ~node_LocalDecl();
};

class node_VarList: public node_LocalDecl{
public:
    node_NameDecl *VarName;
    node_LocalDecl *nextDecl;
public:
    node_VarList(node_NameDecl *VarName, node_LocalDecl *nextDecl);
    ~node_VarList();
    int acceptVisitor(Visitor *v);
};

class node_VarListStmt: public node_Stmt{
public:
    node_VarList *varList;
public:
    node_VarListStmt(node_VarList *VarList);
    ~node_VarListStmt();
    int acceptVisitor(Visitor *v);
};

class node_MethodList: public node_LocalDecl{
public:
    char *methodName;
    node_Type *returnType;
    node_VarList *paramList;
    node_VarList *localVarList;
    node_StmtList *stmtList;
    node_LocalDecl *nextDecl;
    int line;
public:
    node_MethodList(char *MethodName, node_Type *ReturnType, node_VarList *ParamList, node_VarList *LocalVarList, node_StmtList *stmtList, node_LocalDecl *NextDecl, int line);
    ~node_MethodList();
    int acceptVisitor(Visitor *v);
};

class node_ClassList{
public:
    char *className;
    char *extendedClassName;
    node_LocalDecl *firstDecl;
    node_ClassList *nextClass;
    int line;
public:
    node_ClassList(char* ClassName, char* extendedClassName, node_LocalDecl *FirstDecl, node_ClassList *NextClass, int line);
    ~node_ClassList();
    int acceptVisitor(Visitor *v);
};

class node_Program{
public:
    node_ClassList *classList;
public:
    node_Program(node_ClassList *ClassList);
    ~node_Program();
    int acceptVisitor(Visitor *v);
};

#endif // AST_HPP_INCLUDED
